//
//  CLMethod.m
//  RuntimeTest
//
//  Created by Charles on 16/9/26.
//  Copyright © 2016年 Charles. All rights reserved.
//

#import "CLMethod.h"
#import <objc/runtime.h>
#import <objc/message.h>

@implementation CLMethod

- (int)testInstanceMethod:(NSString *)name value:(NSNumber *)value{
    NSLog(@"name is %@",name);
    return value.intValue;
}

- (NSArray *)arrayWithNames:(NSArray *)names{
    NSLog(@"names is %@",names);
    return names;
}

- (void)getMethods{
    unsigned int count = 0;
    Method * methodList = class_copyMethodList(self.class, &count);
    for (unsigned int i = 0; i<count; i++) {
        Method method = methodList[i];
        
        //获取方法名称
        SEL methodName = method_getName(method);
        NSLog(@"方法名称：%@",NSStringFromSelector(methodName));
        
        //获取方法参数的类型
        unsigned int argumentCnt = method_getNumberOfArguments(method);
        char argName[512] = {};
        for (unsigned int i = 0; i<argumentCnt; i++) {
            method_getArgumentType(method, i, argName, 512);
            NSLog(@"第%u个参数类型为：%s",i,argName);
        }
        
        //获取方法返回类型
        char returnType[512] = {};
        method_getReturnType(method, returnType, 512);
    }
    free(methodList);
}

+ (void)test{
    CLMethod * m = [[CLMethod alloc]init];
    
    //    [m getMethods];
    /*
    void (*p)(id,SEL);
    p = (void (*)(id, SEL))objc_msgSend;
    p((id)m,@selector(getMethods));
     */
    ((void (*)(id,SEL))objc_msgSend)((id)m, @selector(getMethods));

    int returnValue = ((int(*)(id,SEL,NSString *,NSNumber *))objc_msgSend)((id)m,@selector(testInstanceMethod:value:),@"iOS底层好屌啊！",@100);
//    int (*q)(id,SEL,NSString *,NSNumber *) = (int(*)(id,SEL,NSString *,NSNumber *))objc_msgSend;
//    returnValue = q((id)m,@selector(testInstanceMethod:value:),@"iOS底层好屌啊！",@200);
    
    // 获取方法
    Method method = class_getInstanceMethod([self class], @selector(testInstanceMethod:value:));
    //调用函数
    returnValue = ((int(*)(id,Method,NSString *,NSNumber *))method_invoke)((id)m,method,@"测试使用method_invoke",@30);

    NSLog(@"return value is %d", returnValue);

}

- (void)originalMethod {
    NSLog(@"原始方法!!");
}

@end

@implementation CLMethodExchange

/**
 测试方法交换
 */
+ (void)testMethodExchange {
    
    SEL originSel = @selector(originalMethod);
    SEL swizzledSel = @selector(cl_origalMethod);
    
    Method originMethod = class_getInstanceMethod(self.class, originSel);
    Method swizzledMethod = class_getInstanceMethod(self.class, swizzledSel);
    
    // 如果原始方法在当前类中存在,didAddMethod的返回值是NO,如果原始方法不在当前类而在其父类中,则返回值是YES.
    // 添加原始方法到当前类中,并且其实现为将要被交换的方法的实现.
    BOOL didAddMethod = class_addMethod(self.class, originSel, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    // 如果原始方法不在当前类中我们就添加一个
    if (didAddMethod) {
        // 修改被交换的方法实现为原始方法的实现.
        class_replaceMethod(self.class, swizzledSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
    }else {
        // 如果都存在就直接交换
        method_exchangeImplementations(originMethod, swizzledMethod);
    }
    
    // 调用原始方法
    CLMethodExchange * methodExchange = [[CLMethodExchange alloc]init];
    [methodExchange originalMethod];
}

/*
- (void)originalMethod {
    NSLog(@"原始方法!!");
}
*/

- (void)cl_origalMethod {
    NSLog(@"调用被交换的方法!!");
    [self cl_origalMethod];
    NSLog(@"被交换的方法调用结束!!");
}

@end
